package com.dt.app.common.utils;

import com.dt.app.common.uploadfile.minio.MinIoUploadFile;
import io.minio.*;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Description minio 单例模式 工具类型、
 **/
public class MinioUtils {

    private static MinioUtils minioUtils = new MinioUtils();

    private MinioClient minioClient;

    private static final String endpoint = "http://xxx.xxx.xxx.xx:9000";
    private static final String accessKey = "xxxxxx"; // 你的 key
    private static final String secretKey = "xxxxxxxxxxxx"; //你的 secret
    private String bucketName = "xxxx"; //桶名称

    private MinioUtils() {
        //this.init();
    }

    /**
     * 初始化创建对象
     */
    private void init() {
        //minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
    }

    /**
     *  获取 MinioUtils 对象
     * @return com.ming.utils.MinioUtils
     **/
    public static MinioUtils getInstance() {
        if(minioUtils.minioClient == null){
            minioUtils.minioClient = MinIoUploadFile.getMinioClient();
        }
        return minioUtils;
    }

    /**
     * Description  获取 MinioClient 对象
     * Create By Mr.Fang
     *
     * @return com.ming.utils.MinioUtils
     * @time 2022/7/10 10:18
     **/
    public MinioClient getMinioClient() {
        return minioClient;
    }
    /**
     * 设置 桶名称
     *
     * @param bucketName
     */
    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    /**
     * 判断桶是否存在
     * @param bucket
     * @return java.lang.Boolean
     **/
    public Boolean existBucket(String bucket) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建一个桶
     * @param bucket 桶名称
     * @return java.lang.Boolean
     **/
    public Boolean createBucket(String bucket) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 上传本地文件
     * @param path   本地文件路径
     * @param object 文件名
     * @return java.lang.Boolean
     **/
    public Boolean uploadObject(String path, String object) {
        try {
            minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(object).filename(path).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 以流的方式上传文件
     * @param in     input 流
     * @param object 文件名
     * @param size   文件大小
     * @return java.lang.Boolean
     **/
    public Boolean uploadObject(InputStream in, String object, Long size) {
        try {
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(object).stream(in, size, -1).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     *  多文件上传
     * @param objects SnowballObject对象
     * @return java.lang.Boolean
     * @Param
     **/
    public Boolean uploadsObject(List<SnowballObject> objects) {
        try {
            minioClient.uploadSnowballObjects(UploadSnowballObjectsArgs.builder().bucket(bucketName).objects(objects).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 批量删除文件对象
     * @param objects 文件名称或完整文件路径
     * @return java.util.List<io.minio.messages.DeleteError>
     **/
    public List<DeleteError> deleteObject(List<String> objects) {
        List<DeleteError> deleteErrors = new ArrayList<>();
        List<DeleteObject> deleteObjects = objects.stream().map(value -> new DeleteObject(value)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build());
        try {
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                deleteErrors.add(error);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deleteErrors;
    }

    /**
     *  下载文件到本地
     * @param object 文件名
     * @param output 输出路径
     * @return void
     **/
    public void download(String object, String output) {

        try {
            minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object(object).filename(output).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 下载 minio文件，返回流的方式，同时支持在线预览
     * @param object   文件名称
     * @param isOnline 是否在线预览，不同文件类型请修改 setContentType
     * @param response 响应对象
     * @return void
     **/
    public void download(String object, Boolean isOnline, HttpServletResponse response) {
        try (InputStream stream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(object).build())) {
            try {
                BufferedInputStream br = new BufferedInputStream(stream);
                byte[] buf = new byte[1024];
                int len = 0;
                response.reset(); // 非常重要
                if (Objects.nonNull(isOnline) && isOnline) { // 在线打开方式
                    response.setContentType("application/pdf");
                    response.setHeader("Content-Disposition", "inline; filename=" + object);
                } else { // 纯下载方式
                    response.setContentType("application/x-msdownload");
                    response.setHeader("Content-Disposition", "attachment; filename=" + object);
                }
                OutputStream out = response.getOutputStream();
                while ((len = br.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
                out.flush();
                br.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有桶
     * @return java.util.List<io.minio.messages.Bucket>
     **/
    public List<Bucket> listBuckets() {

        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件列表
     * @param limit 范围 1-1000
     * @return java.util.List<io.minio.messages.Item>
     **/
    public List<Item> listObjects(int limit) {
        List<Item> objects = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).maxKeys(limit).includeVersions(true).build());
        try {
            for (Result<Item> result : results) {
                objects.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objects;
    }

    /**
     * 生成预览链接，最大7天有效期；
     * 如果想永久有效，在 minio 控制台设置仓库访问规则总几率
     * @param object      文件名称
     * @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf
     * @return java.lang.String
     * @Params
     **/
    public String getPreviewUrl(String object, String contentType) {
        Map<String, String> reqParams = new HashMap<>();
        reqParams.put("response-content-type", contentType != null ? contentType : "application/pdf");
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(object)
                    .expiry(7, TimeUnit.DAYS).extraQueryParams(reqParams).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * 网络文件转储 minio
     * @param httpUrl 文件地址
     * @return void
     * @Params
     **/
    public void netToMinio(String httpUrl) {
        int i = httpUrl.lastIndexOf(".");
        String substring = httpUrl.substring(i);
        URL url;
        try {
            url = new URL(httpUrl);
            URLConnection urlConnection = url.openConnection();
            // agent 模拟浏览器
            urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36");
            DataInputStream dataInputStream = new DataInputStream(url.openStream());

            // 临时文件转储
            File tempFile = File.createTempFile(UUID.randomUUID().toString().replace("-", ""), substring);
            FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = dataInputStream.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
            fileOutputStream.write(output.toByteArray());
            // 上传minio
            uploadObject(tempFile.getAbsolutePath(), tempFile.getName());
            dataInputStream.close();
            fileOutputStream.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件转字节数组
     * @param path 文件路径
     * @return byte[] 字节数组
     **/
    public byte[] fileToBytes(String path) {
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            fis = new FileInputStream(path);
            int temp;
            byte[] bt = new byte[1024 * 10];
            while ((temp = fis.read(bt)) != -1) {
                bos.write(bt, 0, temp);
            }
            bos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (Objects.nonNull(fis)) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bos.toByteArray();
    }

    public static void main(String[] args) throws Exception {
        MinioUtils minioUtils = MinioUtils.getInstance();
        // 多文件上传示例
        List<SnowballObject> objects = new ArrayList<>();
        byte[] bytes1 = minioUtils.fileToBytes("C:\\Users\\Administrator\\Desktop\\素材\\1.png");
        byte[] bytes2 = minioUtils.fileToBytes("C:\\Users\\Administrator\\Desktop\\素材\\2.png");
        byte[] bytes3 = minioUtils.fileToBytes("C:\\Users\\Administrator\\Desktop\\素材\\3.png");
        objects.add(new SnowballObject("1.png", new ByteArrayInputStream(bytes1), bytes1.length, ZonedDateTime.now()));
        objects.add(new SnowballObject("2.png", new ByteArrayInputStream(bytes2), bytes2.length, ZonedDateTime.now()));
        objects.add(new SnowballObject("3.png", new ByteArrayInputStream(bytes3), bytes3.length, ZonedDateTime.now()));
        minioUtils.uploadsObject(objects);
    }
}